#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Adoor.Expression;
using Adoor.Expression.AST;
using Adoor.Relational;
using Adoor.Object.Domain;
using Adoor.Object.Query;

namespace Adoor.Object.Relational
{
    public class ExpressionMapper : ExpressionCloner
    {
        public static ExpressionNode Evaluate(ExpressionNode n, ObjectPath context)
        {
            return Evaluate(n, context, null);
        }

        public static ExpressionNode Evaluate(ExpressionNode n, ObjectPath context, IDictionary<string, RelationalElement> htCorrelationOperators)
        {
            ReferenceData o = null;
            return Evaluate(n, context, false, ref o, htCorrelationOperators);
        }

        public static ExpressionNode Evaluate(ExpressionNode n, ObjectPath context, bool mapReferences, ref ReferenceData refData)
        {
            return Evaluate(n, context, mapReferences, ref refData, null);
        }

        public static ExpressionNode Evaluate(ExpressionNode n, ObjectPath context, bool mapReferences, ref ReferenceData refData, IDictionary<string, RelationalElement> htCorrelationOperators)
        {
            ExpressionMapper es = new ExpressionMapper(context, true, htCorrelationOperators);
            n.Accept(es);
            //refData = es.referenceData;
            refData = null;
            return es.result;
        }

        protected ObjectPath context;
        private Correlation currentCorrelationFilter;
        //private ReferenceData referenceData;
        private bool mapReferences;
        private IDictionary<string, RelationalElement> htCorrelationOperators;

        protected ExpressionMapper(ObjectPath context, bool mapReferences, IDictionary<string, RelationalElement> htCorrelationOperators)
        {
            this.context = context;
            currentCorrelationFilter = null;
            this.mapReferences = mapReferences;
            this.htCorrelationOperators = htCorrelationOperators;
        }


        public override void VisitName(ExpressionName n)
        {
            if (n.Qualifier != "")
            {
                if (this.context != null && n.Value != "*")
                {
                    string qualifier = n.Qualifier;
                    Correlation correlation;
                    if (!context.Correlations.TryGetValue(qualifier, out correlation))
                    {
                        if (currentCorrelationFilter == null)
                            throw new Exception("qualifier " + n.Qualifier + " not found");
                        else
                        {
                            EntityData child = currentCorrelationFilter.EntityData.FindChild(qualifier);
                            if (child == null)
                                throw new Exception("qualifier " + n.Qualifier + " not found");
                            correlation = new Correlation(n.Qualifier, child);
                        }
                    }
                    string value = n.Value;
                    if (!mapReferences || correlation.EntityData.PropertyInfos.ContainsKey(value))
                    {
                        PropertyData propertyInfo = correlation.EntityData.PropertyInfos[value];
                        
                        IPropertyMappingStrategy strategy = (IPropertyMappingStrategy)propertyInfo.GetService(typeof(IPropertyMappingStrategy));
                        if (strategy != null)
                        {
                            this.result = strategy.GetRelationalExpression(n.Qualifier);
                        }
                        else if (propertyInfo.SelectExpression != null)
                        {
                            this.result = new ExpressionName(qualifier, value);
                            //this.result = propertyInfo.SelectExpression.Expr;
                        }
                        else
                            throw new Exception("error in propertyinfo " + value);
                    }
                    else
                    {
                        // reference
                        if (correlation.EntityData.ReferenceInfos.ContainsKey(value))
                        {
                            ReferenceData referenceData;
                            if (!correlation.EntityData.ReferenceInfos.TryGetValue(value, out referenceData))
                                throw new Exception("qualifier " + n.Qualifier + " not found");

                            Correlation cJoin = (Correlation)correlation.LinkedCorrelations[referenceData];
                            Correlation reverseCJoin = (Correlation)correlation.ReversedLinkedCorrelations[referenceData];
                            IReferenceMappingStrategy strategy = (IReferenceMappingStrategy)referenceData.GetService(typeof(IReferenceMappingStrategy));

                            if (cJoin != null)
                            {
                                this.result = strategy.GetRelationalExpression(n.Qualifier, value, cJoin.Name);
                            }
                            else if (reverseCJoin != null)
                            {
                                this.result = strategy.GetRelationalExpression(reverseCJoin.Name, value, n.Qualifier);
                            }
                            else
                            {
                                this.result = strategy.GetRelationalExpression(n.Qualifier, n.Value, n.Value);
                            }
                        }
                        else
                        {
                            if (!correlation.NeededInverseRelations.Contains(value))
                            {
                                correlation.NeededInverseRelations.Add(value);
                            }

                            ReferenceData rd = null;

                            if (this.context.Correlations.ContainsKey(value))
                            {
                                Correlation subc = (Correlation)this.context.Correlations[value];
                                rd = subc.EntityData.ReferenceInfos[n.Qualifier];
                            }
                            if (rd == null)
                            {
                                //TODO:
                                Adoor.Object.Domain.EntityData ed = null;
                                //ed = this.context.Domain.RootDomain.EntityInfos[value];
                                if (ed != null)
                                {
                                    rd = ed.ReferenceInfos[n.Qualifier];
                                }
                            }
                            if (rd == null)
                            {
                                base.VisitName(n);
                            }
                            else
                            {
                                IReferenceMappingStrategy strategy = (IReferenceMappingStrategy)rd.GetService(typeof(IReferenceMappingStrategy));
                                this.result = strategy.GetRelationalExpression(n.Qualifier, n.Value, n.Value);
                            }
                        }
                    }
                }
                else
                {
                    base.VisitName(n);
                }
                return;
            }
            base.VisitName(n);
        }

        public override void VisitCorrelationFilteredNode(ExpressionCorrelationFilteredNode c)
        {
            Correlation oldCorrelation = currentCorrelationFilter;
            try
            {
                currentCorrelationFilter = context.Correlations[c.Qualifier.Qualifier] as Correlation;

                if (currentCorrelationFilter == null)
                    throw new Exception("cannot find correlation \"" + c.Qualifier.Qualifier + "\"");

                base.VisitCorrelationFilteredNode(c);
            }
            finally
            {
                currentCorrelationFilter = oldCorrelation;
            }

            //			if(this.htCorrelationOperators != null)
            //			{
            //				string correlation = c.Qualifier.Qualifier;
            //				AliasOperator alias = (AliasOperator) htCorrelationOperators[correlation];
            //
            //				string s = "";
            //			}
        }

    }
}